<?php
	/*
		----------------------------------------------------------------
		Derived from Easy Reflections by Richard Davey, Core PHP (rich@corephp.co.uk)
		v2 - 2nd March 2007
        Updates include changes by Monte Ohrt (monte@ohrt.com)
		----------------------------------------------------------------
		You are free to use this in any product, or on any web site.
		Latest builds at: http://reflection.corephp.co.uk
		----------------------------------------------------------------

		This script accepts the following $params array parameters:

		img		        	required	The source image (to reflect)
		transparent	    	optional	Generate a transparent reflection (true/false)
		height	        	optional	Height of the reflection (% value)
		bgc		        	optional	Background colour to fade into, default = #000000 (non-transparent reflections only)
        fade_start      	optional    Start the alpha fade from whch value? (% value)
        fade_end        	optional    End the alpha fade from whch value? (% value)
		jpeg	        	optional	Output will be JPEG at 'param' quality (default 80)
        cache           	optional    Save reflection image to the cache? (boolean) (removed, always cache)
	    tint            	optional    Tint the reflection with this colour (hex) (transparent reflections only)
	    resize				optional	resize pct of original (% value)
	    blur 				optional    0-9 creates a second thumbnail image w/blur effect
	    sharpen				optional	0-9 applies a sharpen effect to the thumbnail
	    grayscale			optional 	true=creates a second thumbnail image in grayscale
	    darken				optional    0-9 creates a second thumbnail image w/darken effect
	    memory_limit		optional    temporarily overrides php memory_limit
	    max_exec_time		optional    temporarily overrides php maximum execution time
	*/

// Check to ensure this file is included in Joomla!
defined('_JEXEC') or die( 'Restricted access' );


class thumb
{
	function raiseWarning( $text )
	{
		JError::raiseWarning( 'SOME_ERROR_CODE', get_class($this).": ".$text );
		return false;
	}

	function imageMemoryNeeded( $width, $height, $bits, $channels )
	{
		$memory_needed = round( $width * $height * $bits * $channels / 8 );
		return $memory_needed;
	}

	function setMemoryForImage( $memory_needed )
	{
		if (function_exists('memory_get_usage'))
		{
			$MB = 1048576;  // number of bytes in 1M
			$K64 = 65536;    // number of bytes in 64K
			$TWEAKFACTOR = 1.5;
			$memoryNeeded = round( ( $memory_needed + $K64 ) * $TWEAKFACTOR );
			$memoryLimit = ini_get('memory_limit');
			if ($memoryLimit == '')
			{
				$memoryLimit = 8;
			}
			else
			{

				$memoryLimit = (integer)$memoryLimit;
			}
			$memoryLimitMB = $memoryLimit * $MB;
			if(memory_get_usage() + $memoryNeeded > $memoryLimitMB)
			{
				$newLimit = ceil( ( memory_get_usage() + $memoryNeeded ) / $MB );
				ini_set( 'memory_limit', $newLimit . 'M' );
				return true;
			}
		}
		return false;
	}

	function parseColor( $hexclr, &$red, &$green, &$blue )
	{
		$hex_bgc = $hexclr;

		//    Does it start with a hash? If so then strip it
		$hex_bgc = str_replace('#', '', $hex_bgc);

		switch (strlen($hex_bgc))
		{
			case 6:
				$red = hexdec(JString::substr($hex_bgc, 0, 2));
				$green = hexdec(JString::substr($hex_bgc, 2, 2));
				$blue = hexdec(JString::substr($hex_bgc, 4, 2));
				break;

			case 3:
				$red = JString::substr($hex_bgc, 0, 1);
				$green = JString::substr($hex_bgc, 1, 1);
				$blue = JString::substr($hex_bgc, 2, 1);
				$red = hexdec($red . $red);
				$green = hexdec($green . $green);
				$blue = hexdec($blue . $blue);
				break;

			default:
				//    Wrong values passed
				return false;
				break;
		}
		return true;
	}


	function create( $params )
	{

		//	PHP Version sanity check
		if (version_compare('4.3.2', phpversion()) == 1)
		{
			return $this->raiseWarning( JText::_('This version of PHP is not fully supported. You need 4.3.2 or above.'));
		}

		//	GD check
		if (extension_loaded('gd') == false && !dl('gd.so'))
		{
			return $this->raiseWarning( JText::_('You are missing the GD extension for PHP, unable to continue.'));
		}

		$transparent = (isset($params['transparent']) ? $params['transparent'] : false);

		if ($transparent)
		{
			//    additional checking for transparent images, GD Version check
			$gd_info = gd_info();

			if ($gd_info['PNG Support'] == false)
			{
				//
				//	no transparency support
				//
				$transparent = false;
			}

			if ($transparent && (preg_replace('/[[:alpha:][:space:]()]+/', '', $gd_info['GD Version']) < '2.0.1'))
			{
				//
				//	no transparency support
				//
				$transparent = false;
			}
		}

		$addEffectThumbnail = false;

		$grayscale = (isset($params['grayscale']) ? $params['grayscale'] : false);
		if ($grayscale)
		{
			$addEffectThumbnail = true;
		}
		//	Our allowed query string parameters

		//	img (the image to reflect)
		if (isset($params['img']))
		{
			$source_image = $params['img'];
			$source_image = str_replace('://','',$source_image);

	        if (JFile::exists($source_image))
	        {
                $cache_dir = dirname($source_image);
                $cache_base = basename($source_image);
	        	$cache_base = str_replace('.', '_', $cache_base );
	        	$tmp_array = $params;
	        	if (isset($params['border_width']))
	        	{
	        		$tmp_array['border_width'] = implode( $params['border_width']);
	        	}
	        	if (isset($params['border_color']))
	        	{
	        		$tmp_array['border_color'] = implode( $params['border_color']);
	        	}
	        	$paramlist = implode( $tmp_array ).($transparent ? "1" : "0").($grayscale ? "1" : "0").implode(array_keys($tmp_array));
	        	$cache_file = 'refl_' . $cache_base . '_'. md5($paramlist) . '.jpg';
                $cache_path = $cache_dir . DS . $cache_file;

                if (JFile::exists($cache_path) && filemtime($cache_path) >= filemtime($source_image))
                {
                    // Use cached image
                    return $cache_path;
                }
	        }
	        else
	        {
				return $this->raiseWarning( JText::_('Cannot find or read source image')
					        			  . " Image : "
							      	      . $source_image
								          );
	        }
		}
		else
		{
			return $this->raiseWarning( JText::_('Missing \'img\' parameter'));
		}

		//	bgc (the background colour used, defaults to black if not given)
		$red = 0;
		$green = 0;
		$blue = 0;

		if ( isset($params['bgc']))
		{
			$this->parseColor( $params['bgc'], $red, $green, $blue );
		}

		if ($transparent)
		{
			$red = 127;
			$green = 127;
			$blue = 127;

			if (isset($params['tint']))
			{
				$this->parseColor( $params['tint'], $red, $green, $blue );
			}
		}

		//	height (how tall should the reflection be?)
		$reflection_height_pct = .50;
		if (isset($params['height']))
		{
			$reflection_height_pct = str_replace('%', '', $params['height']);
			$reflection_height_pct = ($reflection_height_pct / 100);
		}

		$resize = 100.0;
		if (isset($params['resize']))
		{
			if (JString::substr($params['resize'], -1) == '%')
			{
				$resize = str_replace('%', '', $params['resize']);
				$resize = ($resize / 100);
			}
			else
			{
				$resized_source_width = (int)$params['resize'];
			}
		}

		if (isset($params['fade_start']))
		{
			if (JString::substr($params['fade_start'], -1) == '%')
			{
				$alpha_start = str_replace('%', '', $params['fade_start']);
				$alpha_start = (int) (127 * $alpha_start / 100);
			}
			else
			{
				$alpha_start = (int) $params['fade_start'];

				if ($alpha_start < 1 || $alpha_start > 127)
				{
					$alpha_start = 80;
				}
			}
		}
		else
		{
			$alpha_start = 80;
		}

		if (isset($params['fade_end']))
		{
			if (JString::substr($params['fade_end'], -1) == '%')
			{
				$alpha_end = str_replace('%', '', $params['fade_end']);
				$alpha_end = (int) (127 * $alpha_end / 100);
			}
			else
			{
				$alpha_end = (int) $params['fade_end'];

				if ($alpha_end < 1 || $alpha_end > 0)
				{
					$alpha_end = 0;
				}
			}
		}
		else
		{
			$alpha_end = 0;
		}

		//	blur
		if (isset($params['blur']))
		{
			$blur = (int) $params['blur'];
			$addEffectThumbnail = true;
		}
		else
		{
			//	No blur
			$blur = 0;
		}

		//	darken
		if (isset($params['darken']))
		{
			$darken = (int) $params['darken'];
			$addEffectThumbnail = true;
		}
		else
		{
			//	Don't darken
			$darken = 0;
		}

		//	sharpen
		if (isset($params['sharpen']))
		{
			$sharpen = (int) $params['sharpen'];
		}
		else
		{
			//	Don't sharpen
			$sharpen = 0;
		}

		$bwidth_top = (isset($params['border_width']['top']) ? (int)$params['border_width']['top'] : 0);
		$bwidth_right = (isset($params['border_width']['right']) ? (int)$params['border_width']['right'] : 0);
		$bwidth_bottom = (isset($params['border_width']['bottom']) ? (int)$params['border_width']['bottom'] : 0);
		$bwidth_left = (isset($params['border_width']['left']) ? (int)$params['border_width']['left'] : 0);
		$bcolor_top = (isset($params['border_color']['top']) ? $params['border_color']['top'] : '#000000');
		$bcolor_right = (isset($params['border_color']['right']) ? $params['border_color']['right'] : '#000000');
		$bcolor_bottom = (isset($params['border_color']['bottom']) ? $params['border_color']['bottom'] : '#000000');
		$bcolor_left = (isset($params['border_color']['left']) ? $params['border_color']['left'] : '#000000');

		/*
			----------------------------------------------------------------
			Ok, let's do it ...
			----------------------------------------------------------------
		*/

		//	How big is the image?
		$image_details = getimagesize($source_image);

		if ($image_details === false)
		{
			return $this->raiseWarning( JText::_('Not a valid image supplied or this script does not have permissions to access it.')
				                      . " Image "
				                      .$source_image
				                      );
		}
		else
		{
			$width = $image_details[0];
			$height = $image_details[1];
			$type = $image_details[2];
		}


		if (isset($resized_source_width))
		{
			$resized_source_width = min( $width, $resized_source_width );
			$pct = ($height / $width);
			$resized_source_height = (int)($resized_source_width * $pct);
		}
		else
		{
			if ($resize !== 100.0)
			{
				$resized_source_width = round($width * $resize);
				$resized_source_height = round($height * $resize);
			}
			else
			{
				$resized_source_width = $width;
				$resized_source_height = $height;
			}
		}

		//	calculate the thumbnail height
		$reflection_height = (int)($resized_source_height * $reflection_height_pct);

		if (isset($params['memory_limit']))
		{
			//	force memory limit override
			ini_set( "memory_limit", $params['memory_limit'] );
		}
		else
		{
			if (!isset($image_details['channels']))
			{
				$image_details['channels'] = 3;
			}
			if (!isset($image_details['bits']))
			{
				$image_details['bits'] = 8;
			}
			$memory_needed = $this->imageMemoryNeeded( $resized_source_width + $bwidth_left + $bwidth_right
				                                     , $resized_source_height + $bwidth_top + $bwidth_bottom
				                                     , $image_details['bits']
				                                     , $image_details['channels']
				                                     );
			$memory_needed2 = $memory_needed;
			$memory_needed += $this->imageMemoryNeeded( $image_details[0]
				                                      , $image_details[1]
				                                      , $image_details['bits']
				                                      , $image_details['channels']
				                                      );

			$memory_needed2 += $this->imageMemoryNeeded( $resized_source_width + $bwidth_left + $bwidth_right
				                                       , $resized_source_height + $reflection_height + $bwidth_top + $bwidth_bottom
				                                       , $image_details['bits']
				                                       , $image_details['channels']
				                                       );
			if ($addEffectThumbnail)
			{
				$memory_needed2 *= 2;
			}
			$memory_needed2 += ($this->imageMemoryNeeded( $resized_source_width + $bwidth_left + $bwidth_right
				                                        , $reflection_height + $bwidth_bottom
				                                        , $image_details['bits']
				                                        , $image_details['channels'] ) * 2
				                                        );

			//	try to anticipate memory needs
			$this->setMemoryForImage( max( $memory_needed, $memory_needed2) );
		}

		// override php max_execution_time setting
		if (isset($params['max_exec_time']))
		{
			ini_set( "max_execution_time", $params['max_exec_time']);
		}

		//
		//	since we can't trap GD errors, force error output from here on...
		//
		ini_set("display_errors", "on");

		//	Calculate the height of the output image
		//	Detect the source image format - only GIF, JPEG and PNG are supported. If you need more, extend this yourself.
		switch ($type)
		{
			case 1:
				//	GIF
				$source = imagecreatefromgif($source_image);
				break;

			case 2:
				//	JPG
				$source = imagecreatefromjpeg($source_image);
				break;

			case 3:
				//	PNG
				$source = imagecreatefrompng($source_image);
				break;

			default:
				return $this->raiseWarning( JText::_('Unsupported image file format.')
										  . " Type "
										  . $type
										  . " Image "
										  . $source_image
					                      );
		}

		/*
		   ----------------------------------------------------------------
		   Resize the source image
		   ----------------------------------------------------------------
		*/
		//	create a resized version of the source image
		$newsource = imagecreatetruecolor( $resized_source_width + $bwidth_left + $bwidth_right, $resized_source_height + $bwidth_top + $bwidth_bottom);
		$bred = 0;
		$bgreen = 0;
		$bblue = 0;
		$this->parseColor( $bcolor_top, $bred, $bgreen, $bblue );
		if ($transparent)
		{
			imagesavealpha($newsource, true);
			imagealphablending($newsource, false );
		}
		imagefilledrectangle($newsource, 0, 0, $resized_source_width + $bwidth_left + $bwidth_right, $resized_source_height + $bwidth_top + $bwidth_bottom, imagecolorallocatealpha($newsource, $bred, $bgreen, $bblue, 0));
		imagecopyresized( $newsource, $source, $bwidth_left, $bwidth_top, 0, 0, $resized_source_width, $resized_source_height, $width, $height );
		//	get rid of the source image as we no longer need it
		imagedestroy( $source );

		if ($bwidth_bottom && $bcolor_bottom != $bcolor_top)
		{
			if ($this->parseColor( $bcolor_bottom, $bred, $bgreen, $bblue ))
			{
				imagefilledrectangle($newsource, 0, $resized_source_height + $bwidth_top, $resized_source_width+$bwidth_left+$bwidth_right, $resized_source_height+$bwidth_bottom+$bwidth_top, imagecolorallocatealpha($newsource, $bred, $bgreen, $bblue, 0));
			}
		}
		if ($bwidth_left && $bcolor_left != $bcolor_top)
		{
			if ($this->parseColor( $bcolor_left, $bred, $bgreen, $bblue ))
			{
				imagefilledrectangle($newsource, 0, $bwidth_top/2, $bwidth_left, $resized_source_height+(($bwidth_bottom+$bwidth_top)/2), imagecolorallocatealpha($newsource, $bred, $bgreen, $bblue, 0));
			}
		}
		if ($bwidth_right && $bcolor_right != $bcolor_top)
		{
			if ($this->parseColor( $bcolor_right, $bred, $bgreen, $bblue ))
			{
				imagefilledrectangle($newsource, $resized_source_width+$bwidth_left, $bwidth_top/2, $resized_source_width+$bwidth_left+$bwidth_right, $resized_source_height+(($bwidth_bottom+$bwidth_top)/2), imagecolorallocatealpha($newsource, $bred, $bgreen, $bblue, 0));
			}
		}

		$resized_source_width += $bwidth_left + $bwidth_right;
		$resized_source_height += $bwidth_top + $bwidth_bottom;
		$reflection_height += $bwidth_bottom;

		if ($sharpen)
		{
			$sharpen = (10-$sharpen)*10;
			$array = array( array( -1,-1,-1), array(-1,$sharpen,-1), array(-1,-1,-1));
			imageconvolution( $newsource, $array, $sharpen-8, 0 );
		}

		if ($reflection_height > 0)
		{
			//	We'll store the final reflection in $output. $buffer is for internal use.
			$output = imagecreatetruecolor( $resized_source_width, $reflection_height);
			$buffer = imagecreatetruecolor( $resized_source_width, $reflection_height);
			if ($transparent)
			{
				imagesavealpha($output, true);
				imagealphablending($output, false );
				imagesavealpha($buffer, true);
				imagealphablending($buffer, false );
			}

			//	Copy the bottom-most part of the source image into the output
			imagecopy($output, $newsource, 0, 0, 0, $resized_source_height - $reflection_height, $resized_source_width, $reflection_height);

			//	Create the reflection image, Rotate and flip it (strip flip method)
		    for ($y = 0; $y < $reflection_height; $y++)
		    {
		       imagecopy( $buffer, $output, 0, $y, 0, $reflection_height - $y - 1, $resized_source_width, 1);
		    }
			imagedestroy($output);
			$output = $buffer;

			/*
				----------------------------------------------------------------
				Apply the fade effect
				----------------------------------------------------------------
			*/

			//	This is quite simple really. There are 127 available levels of alpha, so we just
			//	step-through the reflected image, drawing a box over the top, with a set alpha level.
			//	The end result? A cool fade into the background colour given.

			//	There are a maximum of 127 alpha fade steps we can use, so work out the alpha step rate

			$alpha_length = abs($alpha_start - $alpha_end);

			if ($transparent)
			{
				imagelayereffect($output, IMG_EFFECT_OVERLAY);
			}

			for ($y = 0; $y <= $reflection_height; $y++)
			{
				//  Get % of reflection height
				$pct = $y / $reflection_height;

				//  Get % of alpha
				if ($alpha_start > $alpha_end)
		        {
		            $alpha = (int) ($alpha_start - ($pct * $alpha_length));
		        }
		        else
		        {
		            $alpha = (int) ($alpha_start + ($pct * $alpha_length));
		        }

				if ($transparent)
				{
					$alpha = 127 - $alpha;
				}
				imagefilledrectangle($output, 0, $y, $resized_source_width, $y, imagecolorallocatealpha($output, $red, $green, $blue, $alpha));
			}
		}

		if ($addEffectThumbnail)
		{
			$finaloutput = imagecreatetruecolor($resized_source_width, ($resized_source_height+$reflection_height)*2);
		}
		else
		{
			$finaloutput = imagecreatetruecolor($resized_source_width, $resized_source_height+$reflection_height);
		}

		if ($transparent)
		{
			imagesavealpha($finaloutput, true);
			imagealphablending($finaloutput, false );
			$trans_color = imagecolorallocatealpha($finaloutput, 0, 0, 0, 127);
			imagefill($finaloutput, 0, 0, $trans_color);
		}

		//	put resized image + reflection in the output buffer
		imagecopy($finaloutput, $newsource, 0, 0, 0, 0, $resized_source_width, $resized_source_height);
		if ($reflection_height > 0)
		{
			imagecopy($finaloutput, $output, 0, $resized_source_height, 0, 0, $resized_source_width, $reflection_height);
			imagedestroy($output);
		}

		if ($addEffectThumbnail)
		{
			//	apply the special effects to the resized image and recreate the reflection
			if ($grayscale)
			{
				imagefilter($newsource, IMG_FILTER_GRAYSCALE );
			}
			if ($darken > 0 && $blur == 0)
			{
				$array = array( array( 0,0,0), array(0,16,0), array(0,0,0));			// darken
				imageconvolution( $newsource, $array, 16+($darken*10), 0 );
			}

			if ($blur > 0)
			{
				$darken2 = 0;
				for ($i = 0; $i < $blur; $i++)
				{
//					imagefilter($newsource, IMG_FILTER_GAUSSIAN_BLUR );
					$array = array( array( 1,2,1), array(2,4,2), array(1,2,1));			//	gaussian blur
					if (($i+1)==$blur )
					{
						//	do darkening on last blur pass
						$darken2 = $darken*10;
					}
					imageconvolution( $newsource, $array, 16+$darken2, 0 );
				}
			}
			if ($reflection_height > 0)
			{
				$output = imagecreatetruecolor( $resized_source_width, $reflection_height);
				$buffer = imagecreatetruecolor( $resized_source_width, $reflection_height);
				if ($transparent)
				{
					imagesavealpha($output, true);
					imagealphablending($output, false );
					imagesavealpha($buffer, true);
					imagealphablending($buffer, false );
				}

				//	Copy the bottom-most part of the source image into the output
				imagecopy($output, $newsource, 0, 0, 0, $resized_source_height - $reflection_height, $resized_source_width, $reflection_height);
				//	Create the reflection image from , Rotate and flip it (strip flip method)
				for ($y = 0; $y < $reflection_height; $y++)
				{
					imagecopy( $buffer, $output, 0, $y, 0, $reflection_height - $y - 1, $resized_source_width, 1);
				}
				$output = $buffer;
				if ($transparent)
				{
					imagelayereffect($output, IMG_EFFECT_OVERLAY);
				}
				// 	apply the fade to the reflection
				for ($y = 0; $y <= $reflection_height; $y++)
				{
					//  Get % of reflection height
					$pct = $y / $reflection_height;

					//  Get % of alpha
					if ($alpha_start > $alpha_end)
					{
						$alpha = (int) ($alpha_start - ($pct * $alpha_length));
					}
					else
					{
						$alpha = (int) ($alpha_start + ($pct * $alpha_length));
					}

					if ($transparent)
					{
						$alpha = 127 - $alpha;
					}

					imagefilledrectangle($output, 0, $y, $resized_source_width, $y, imagecolorallocatealpha($output, $red, $green, $blue, $alpha));
				}
			}
			//	copy resized source image + effects into the output buffer
			imagecopy( $finaloutput, $newsource, 0, $resized_source_height+$reflection_height, 0, 0, $resized_source_width, $resized_source_height+$reflection_height );
			if ($reflection_height > 0)
			{
				// 	copy the reflection to the output buffer
				imagecopy($finaloutput, $output, 0, ($resized_source_height*2)+$reflection_height, 0, 0, $resized_source_width, $reflection_height);
			}
		}

		/*
			----------------------------------------------------------------
			Output our final PNG
			----------------------------------------------------------------
		*/

		//	If you'd rather output a JPEG instead of a PNG then pass the parameter 'jpeg' (no value needed) on the querystring

		if ($transparent || isset($params['png']))
		{
         	imagepng($finaloutput, $cache_path);
			imagedestroy($finaloutput);
            return $cache_path;
		}
		else
		{
			$quality = 90;
			if (isset($params['jpeg']))
			{
				$qualstr = $params['jpeg'];
				if (JString::substr($qualstr, -1) == '%')
				{
					$qualstr = str_replace('%', '', $qualstr);
				}
				$quality = (int)$qualstr;
			}

			if ($quality < 1 || $quality > 100)
			{
				$quality = 90;
			}

			//	JPEG (the final parameter = the quality, 0 = terrible, 100 = pixel perfect)
            imagejpeg($finaloutput, $cache_path, $quality);
			imagedestroy($finaloutput);
            return $cache_path;
		}
	}
}
?>